#ifndef IMAGE_PRECESSING_HPP
#define IMAGE_PRECESSING_HPP
#include <iostream>
#include <math.h>
#include <functional>
#include <opencv2/opencv.hpp>
#include <algorithm>
#include "rt_mem_fly.hpp"
#include <algorithm>
#include <chrono>
namespace algocommon
{

    class ProcessComposeMat
    {
    public:
        using ProcessFunction = std::function<cv::Mat(const cv::Mat &)>;

    public:
        ProcessComposeMat() = default;

        ProcessComposeMat(const std::vector<ProcessFunction> &process_fun_compose) : process_fun_compose_(process_fun_compose){};
        virtual ~ProcessComposeMat()
        {
            process_fun_compose_.clear();
        };

    public:
        cv::Mat operator()(const cv::Mat &img)
        {

            cv::Mat temp = img;
            for (auto &fun : process_fun_compose_)
            {
                temp = fun(temp);
            }
            return temp;
        }

        void ResetCompose(std::vector<ProcessFunction> &funcompose)
        {

            process_fun_compose_ = funcompose;
        }

    private:
        std::vector<ProcessFunction> process_fun_compose_;
    };

    class MatPad
    {
    public:
        MatPad(const int cutw, const int cuth, int sizemax) : cutw_(cutw), cuth_(cuth), sizemax_(sizemax) {}
        ~MatPad() = default;

        cv::Mat operator()(const cv::Mat &img)
        {
            cv::Mat temp = cv::Mat::zeros(sizemax_, sizemax_, CV_8UC3);
            cv::Mat img_result;
            cv::Rect roi;
            roi = cv::Rect(0, 0, cutw_, cuth_);

            cv::resize(img, img_result, cv::Size(cutw_, cuth_), 0, 0, 1);

            img_result.copyTo(temp(roi));

            return temp;
        }

    private:
        int cutw_;
        int cuth_;
        int sizemax_;
    };

    class MatPadValue
    {

    public:
        MatPadValue(const int cutw, const int cuth, int sizemax, int value) : cutw_(cutw), cuth_(cuth), sizemax_(sizemax), value_(value) {}
        ~MatPadValue() = default;

        cv::Mat operator()(const cv::Mat &img)
        {
            // cv::Mat temp = cv::Mat::zeros(sizemax_,sizemax_,CV_8UC3);
            cv::Mat temp(sizemax_, sizemax_, CV_8UC3, cv::Scalar(value_, value_, value_));

            cv::Mat img_result;

            cv::Rect roi;

            roi = cv::Rect(0, 0, cutw_, cuth_);

            cv::resize(img, img_result, cv::Size(cutw_, cuth_), 0, 0, 1);

            img_result.copyTo(temp(roi));

            // cv::imshow("temp",temp);

            // cv::waitKey();

            return temp;
        }

    private:
        int cutw_;
        int cuth_;
        int sizemax_;
        int value_;
    };

    class MatNormalizeDiv
    {
    public:
        MatNormalizeDiv(){};
        ~MatNormalizeDiv() = default;

        cv::Mat operator()(const cv::Mat &img)
        {
            cv::Mat img_float;
            if (img.type() == CV_32FC3)
                img_float = img;
            else if (img.type() == CV_8UC3)
            {
                std::cout << "CV_8UC3 convert to CV_32FC3" << std::endl;
                img.convertTo(img_float, CV_32FC3);
            }
            else
                assert(0);
            img_float /= 255.;
            return img_float;
        };
    };

    class MatResize
    {
    public:
        MatResize(int w, int h, int interpolation = 1) : w_(w), h_(h), interpolation_(interpolation) {}
        ~MatResize() = default;

        cv::Mat operator()(const cv::Mat &img)
        {
            // std::cout<<"tets resize"<<std::endl;
            cv::Mat sized;
            cv::resize(img, sized, cv::Size(w_, h_), interpolation_);
            return sized;
        }

    private:
        // cv::Size size_;
        int interpolation_;
        int w_;
        int h_;
    };

    class MatNormalize
    {
    public:
        MatNormalize(const std::vector<float> &mean, const std::vector<float> &std, bool div) : mean_(mean), std_(std), div_(div) {}
        ~MatNormalize() = default;

        cv::Mat operator()(const cv::Mat &img)
        {
            cv::Mat img_float;
            if (img.type() == CV_32FC3)
                img_float = img;
            else if (img.type() == CV_8UC3)
            {
                std::cout << "convertto" << std::endl;
                img.convertTo(img_float, CV_32FC3);
            }
            else
                assert(0);

            int height = img.rows;
            int width = img.cols;

            if (div_)
                img_float = img_float / 255.;

            cv::Mat mean = cv::Mat(cv::Size(width, height), CV_32FC3, cv::Scalar(mean_[0], mean_[1], mean_[2]));
            cv::Mat std = cv::Mat(cv::Size(width, height), CV_32FC3, cv::Scalar(std_[0], std_[1], std_[2]));

            cv::Mat sample_sub;
            cv::subtract(img_float, mean, sample_sub);
            cv::Mat sample_normalized = sample_sub / std;
            return std::move(sample_normalized);
        }

    private:
        std::vector<float> mean_;
        std::vector<float> std_;
        bool div_ = false;
    };

    class MatCvtColor
    {
    public:
        MatCvtColor(int code) : code_(code) {}
        ~MatCvtColor() = default;
        cv::Mat operator()(const cv::Mat &img)
        {
            if (img.channels() != 3)
            {
                printf("input images channels need 3\n");
                assert(0);
            }
            cv::Mat res;
            cv::cvtColor(img, res, code_);
            return res;
        }

    private:
        int code_;
    };

    template <typename Dtype>
    inline Dtype sigmoid(Dtype x)
    {
        return 1.f / (1.f + exp(-x));
    }

    class TensorFly2CPU
    {
    public:
        TensorFly2CPU() = default;
        // ~TensorFly2CPU()=default;

    public:
        std::vector<cv::Mat> operator()(TensorFloat &input_tensor)
        {

            std::vector<cv::Mat> channels_vec;

            float *data = input_tensor.mutable_cpu_data();

            int channels = input_tensor.GetChannels();
            int width = input_tensor.GetWidth();
            int height = input_tensor.GetHeight();

            // std::cout<<"channels:"<<channels<<std::endl;

            for (int i = 0; i < channels; i++)
            {

                cv::Mat input_mat = cv::Mat(height, width, CV_32FC1, data);

                channels_vec.emplace_back(input_mat);

                data += width * height;
            }

            return std::move(channels_vec);
        }
    };

    template <class T>
    inline void MatCopyToCudaTensor(T &target, const cv::Mat &image)
    {

        target.Resize(1, image.channels(), image.rows, image.cols); //N C H W

        checkCudaErrors(cudaMemcpy(target.mutable_gpu_data(), reinterpret_cast<unsigned char *>(image.data),
                                   target.GetSize(),
                                   cudaMemcpyHostToDevice));
    }

    //    inline void MatCopyToCudaTensorWithPadding(TensorUint8 &src_tensor, \
//                                                 TensorUint8 &padding_tensor, \
//                                                 const cv::Mat &image, \
//                                                 const cv::Mat &temp)
    //    {

    //         src_tensor.Resize(1, image.channels(), image.rows, image.cols);//N C H W
    //         checkCudaErrors(cudaMemcpy(src_tensor.mutable_gpu_data(), reinterpret_cast<unsigned char*>(image.data),
    //         src_tensor.GetSize(),
    //         cudaMemcpyHostToDevice));

    //         padding_tensor.Resize(1, temp.channels(), temp.rows, temp.cols);//N C H W
    //         checkCudaErrors(cudaMemcpy(padding_tensor.mutable_gpu_data(), reinterpret_cast<unsigned char*>(temp.data),
    //         padding_tensor.GetSize(),
    //         cudaMemcpyHostToDevice));

    //    }

} //namespace algocommon

#endif